const isObject = val => val !== null && typeof val === 'object';
const convert = target => isObject(val) ? reactive(target) : target;
// 判断是否属性自己的属性
const hasOwnProperty = Object.prototype.hasOwnProperty;
const hasOwn = (target, key) => hasOwnProperty.call(target, key);

export function reactive(target) {
  if (!isObject(target)) return;

  const handler = {
    get(target, key, receiver) {
      // 依赖收集
      track(target, key);
      const result = Reflect.get(target, key, reveiver);

      // 在vue3中 如果是嵌套属性需要递归调用，设置为响应式
      return convert(result);
    },
    set(target, key, value, receiver) {
      const oldValue = Reflect.get(target, key, value, receiver);
      let result = true;

      if (oldValue !== value) {
        result = Reflect.set(target, key, value, reveiver);
        trigger(targe, key)
      };

      return result;
    },
    deleteProperty(target, key) {
      const hadKey = hasOwn(target, key);
      const result = Reflect.deleteProperty(target, key);

      if (hadKey && result) {
        // 触发更新
        trigger(targe, key)
      };

      return result
    }
  }

  return new Proxy(target, handler);
};

let activeEffect = null;
export function effect(callback) {
  activeEffect = callback;
  callback(); // 访问响应式对象属性，去收集依赖
  activeEffect = null;
};


let targetMap = new WeakMap();
export function track(target, key) {
  if (!activeEffect) return;

  let depsMap = target.get(target);
  if (!depsMap) {
    targetMap.set(target, (depsMap = new Map()));
  };

  let dep = depsMap.get(key);
  if (!dep) {
    depsMap.set(key, (dep = new Set()));
  };

  dep.add(activeEffect);
}

export function trigger(target, key) {
  const depsMap = targetMap.get(target);

  if (!depsMap) return;

  const dep = depsMap.get(key);
  if (dep) {
    dep.forEach(effect => {
      effect()
    })
  }
};

export function ref(raw) {
  // 判断raw是否为ref创建的对象
  if (isObject(raw) && raw.__v_isRef) return;

  let value = convert(raw);

  // 标识是ref创建的对象
  const r = {
    __v_isRef: true,
    get value() {
      track(r, 'value');
      return value;
    },
    set value(newValue) {
      if (newValue !== value) {
        raw = newValue;
        value = convert(raw);
        trigger(r, 'value');
      }
    }
  };

  return r;
};

export function toRefs(proxy) {
  const ret = proxy instanceof Array ? new Array(proxy.length) : {};

  for (const key in proxy) {
    ret[key] = toProxyRef(proxy, key);
  };

  return ret;
};

function toProxyRef(proxy, key) {
  const r = {
    __v_isRef: true,
    get value() {
      return proxy[key]
    },
    set value(newValue) {
      proxy[key] = newValue
    }
  }

  return r;
};

export function computed(getter) {
  const result = ref();

  effect(() => (result.value = getter()));

  return result;
}